home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C++ / Frameworks / Sprocket Framework DR2 / Sprocket Framework / DocumentWindow.cp < prev    next >
Text File  |  1996-06-12  |  22KB  |  981 lines

  1. /*
  2.  
  3.     File:        UDocumentWindow.cp
  4.     Project:    Sprocket Framework 1.1 of 6/13/96
  5.     Contains:    Code which handles the standard functions required of a document-based window
  6.     To Do:        ?
  7.  
  8.     Sprocket Major Contributors:
  9.     ----------------------------
  10.     Dave Falkenburg, producer of Sprocket 1.0
  11.     Bill Hayden,     producer of Sprocket 1.1
  12.     Steve Sisak,     producer of the upcoming Sprocket 2.0
  13.     
  14.     Pete Alexander        Steve Falkenburg    Randy Thelen
  15.     Eric Berdahl        Nitin Ganatra        Chris K. Thomas
  16.     Marshall Clow        Dave Hershey        Leonard Rosenthal
  17.     Tim Craycroft        Dave Mark            Dean Yu
  18.     David denBoer        Gary Powell
  19.     Cameron Esfahani    Jon Summers            Apple Computer, Inc.
  20.         
  21.     Comments, Additions, or Corrections:
  22.     ------------------------------------
  23.     Bill Hayden, Nikol Software <nikol@codewell.com>
  24.  
  25. */
  26.  
  27. #include "DocumentWindow.h"
  28.  
  29. #include "Sprocket.h"
  30. #include "StandardMenus.h"
  31. #include "UDialog.h"
  32. #include "UString.h"
  33. #include "UPrinting.h"
  34.  
  35. #include <GXExceptions.h>
  36. #include <GXEnvironment.h>
  37. #include <GXGraphics.h>
  38. #include <Printing.h>
  39. #include <Errors.h>
  40.  
  41. TDynamicArray    TDocumentWindow::fgActiveDocuments;
  42.  
  43.  
  44. static OSErr ReplaceCollectionItem(void *newData, long collectSize,
  45.                                    OSType collectType, long collectID,
  46.                                    Collection whichCollection,
  47.                                    Ptr *oldData, long *oldDataSize);
  48.                                    
  49. static OSErr PrintOneGXShape(gxShape currentShape, long refCon);
  50.  
  51. static OSErr CheckObjectLock(short vRefNum, long dirID, StringPtr name);
  52.  
  53.  
  54.  
  55. /**********************************************************************************/
  56.  
  57.  
  58.  
  59. TDocumentWindow::TDocumentWindow(FSSpecPtr theFile /* = NULL */)
  60. {
  61.     OSErr    err;
  62.     
  63.     
  64.     fgActiveDocuments.InsertFirst(this);
  65.  
  66.     if (theFile)
  67.         {
  68.         fDirty = false;
  69.         fHasBeenSaved = true;
  70.         fHasNewEditions = false;
  71.         
  72.         fBackingFile = new TFile(*theFile);
  73.         }
  74.     else
  75.         {
  76.         fDirty = true;
  77.         fHasBeenSaved = false;
  78.         fHasNewEditions = false;
  79.         }
  80.         
  81.     pcpy(fDocumentKind, "\p");
  82.     pcpy(fDocumentName, "\pUntitled");
  83.     
  84.     if (gHasQuickDrawGX)
  85.         {
  86.         err = GXNewJob(&fPrintJob);
  87.  
  88.         if (err == noErr)
  89.             GXInstallApplicationOverride(fPrintJob,
  90.                                          gxPrintingEventMsg,
  91.                                          NewGXPrintingEventProc(PrintingEventOverrideForGX));
  92.                                          
  93.         fPageFormats = new TDynamicArray;
  94.         }
  95.     else
  96.         {
  97.         fPrintRecord = (THPrint) NewHandle(sizeof(TPrint));
  98.         err = MemError();
  99.  
  100.         if (!err)
  101.             {
  102.             PrOpen();
  103.             PrintDefault(fPrintRecord);
  104.             PrClose();
  105.             }
  106.         }
  107.         
  108.     if (err)
  109.         {
  110.         fCanPrint = false;
  111.         ErrorReporter(err, __FILE__, __LINE__);
  112.         }
  113.     else
  114.         fCanPrint = true;
  115. }
  116.  
  117.  
  118.  
  119. /**********************************************************************************/
  120.  
  121.  
  122.  
  123. TDocumentWindow::~TDocumentWindow()
  124. {
  125.     fgActiveDocuments.FindAndDeleteElement(this);
  126. }
  127.  
  128.  
  129.  
  130. /**********************************************************************************/
  131.  
  132.  
  133.  
  134. Boolean TDocumentWindow::DoCommand(CommandID command)
  135. {
  136.     switch (command)
  137.         {
  138.         case    cSave:
  139.             if (fHasBeenSaved)
  140.                 this->Save();
  141.             else
  142.                 this->SaveAs();
  143.             break;
  144.             
  145.         case    cSaveAs:
  146.             this->SaveAs();
  147.             break;
  148.             
  149.         case    cPageSetup:
  150.         case    cCustomPageSetup:
  151.             this->PageSetup(command == cCustomPageSetup);
  152.             break;
  153.         
  154.         case    cPrint:
  155.         case    cPrintOne:
  156.             this->Print(command == cPrint);
  157.             break;
  158.  
  159.         default:
  160.             return false;
  161.         }
  162.  
  163.     return true;
  164. }
  165.  
  166.  
  167.  
  168. /**********************************************************************************/
  169.  
  170.  
  171.  
  172. Boolean TDocumentWindow::Close(void)
  173. {
  174.     return TWindow::Close();
  175. }
  176.  
  177.  
  178.  
  179. /**********************************************************************************/
  180.  
  181.  
  182.  
  183. Boolean TDocumentWindow::CanClose(Boolean    quitting)
  184. {
  185.     SetCursor(&qd.arrow);    // in case the user hit cmd-W while over the text area
  186.     
  187.     if (fDirty)
  188.         {
  189.         Str255    actionStr;
  190.         short    reply, SaveAlertIndex;
  191.         OSErr    err;
  192.         
  193.         GetIndString(actionStr, kStandardCloseStrings, quitting ? kQuittingStr : kClosingStr);
  194.         ParamText(this->fDocumentKind, this->fDocumentName, actionStr, "\p");
  195.         
  196.         if (fHasNewEditions)
  197.             SaveAlertIndex = kStandardCloseWithNewPubsAlertID;
  198.         else
  199.             SaveAlertIndex = kStandardCloseAlertID;
  200.             
  201.         reply = StandardAlert(SaveAlertIndex, 1, 2);
  202.         
  203.         switch (reply)
  204.             {
  205.             case 3:        // Don't Save
  206.                 return true;
  207.                 break;
  208.                     
  209.             case 2:        // Cancel
  210.                 return false;
  211.                 break;
  212.                 
  213.             case 1:        // OK (i.e. save the document)
  214.                 if (fHasBeenSaved)
  215.                     err = this->Save();
  216.                 else
  217.                     err = this->SaveAs();
  218.                     
  219.                 ErrorReporter(err, __FILE__, __LINE__);
  220.                 break;
  221.             }
  222.         }
  223.  
  224.     return true;
  225. }
  226.  
  227.  
  228.  
  229. /**********************************************************************************/
  230.  
  231.  
  232.  
  233. Boolean TDocumentWindow::DeleteAfterClose(void)
  234. {
  235.     if (fPrintRecord != nil)
  236.         DisposeHandle((Handle) fPrintRecord);
  237.     
  238.     if (fPrintJob != nil)
  239.         GXDisposeJob(fPrintJob);
  240.  
  241.     return true;
  242. }
  243.  
  244.  
  245.  
  246. /**********************************************************************************/
  247.  
  248.  
  249.  
  250. Boolean TDocumentWindow::CloseAllDocuments(Boolean quitting)
  251. {
  252.     TDocumentWindow    *aDocument;
  253.     
  254.     aDocument = (TDocumentWindow *) fgActiveDocuments.GetIndexedElement(0);
  255.     
  256.     while (aDocument != nil)
  257.         {
  258.         if (aDocument->CanClose(quitting))
  259.             aDocument->Close();
  260.         else
  261.             break;
  262.             
  263.         aDocument = (TDocumentWindow *) fgActiveDocuments.GetIndexedElement(0);
  264.         }
  265.  
  266.     //    If we got through all the documents, return true
  267.     return (aDocument == NULL);
  268. }
  269.  
  270.  
  271.  
  272. /**********************************************************************************/
  273.  
  274.  
  275.  
  276. void TDocumentWindow::Activate(Boolean activating)
  277. {
  278.     if (activating)
  279.         fgActiveDocuments.MoveToFront((ArrayElementPtr) this);
  280. }
  281.  
  282.  
  283.  
  284. /**********************************************************************************/
  285.  
  286.  
  287.  
  288. #pragma mark === Filing Methods ===
  289.  
  290.  
  291. OSErr TDocumentWindow::Save()
  292. {
  293.     return -1;
  294. }
  295.  
  296.  
  297.  
  298. /**********************************************************************************/
  299.  
  300.  
  301.  
  302. OSErr TDocumentWindow::SaveAs()
  303. {
  304.     FInfo                fileInfo;
  305.     StandardFileReply    reply;
  306.     OSErr                err;
  307.     unsigned long        theTime;
  308.     short                dataForkRefNum = 0;
  309.     short                resForkRefNum = 0;
  310.     Str255                tempFileName;
  311.     FSSpec                tempFileSpec;
  312.     short                tempVRef;        // volume reference # for the temp file
  313.     long                tempDirID;        // directory ID of the temp file
  314.     
  315.     TFile*                tempFile;
  316.     
  317.     
  318.     StandardPutFile("\pSave this document as:", this->fDocumentName, &reply);
  319.     if (!reply.sfGood)
  320.         return userCanceledErr;
  321.  
  322.     if ( reply.sfReplacing )
  323.         {
  324.         err = CheckObjectLock(reply.sfFile.vRefNum, reply.sfFile.parID, (StringPtr) reply.sfFile.name);
  325.         if ( err != noErr ) // if it returns noErr, the file/directory is NOT locked
  326.             return err;
  327.     
  328.         // create the temporary file name.  the name doesn't have to make sense, just
  329.         // be unique
  330.         
  331.         GetDateTime( &theTime );
  332.         NumToString( theTime, tempFileName );
  333.     
  334.         // find the temporary items folder on the file's volume; create it if necessary
  335.         // it is important that the temp folder (and the temp file) and the "original" target
  336.         // file be on the same volume; if not, FSpExchangeFiles will return diffVolErr (-1303)
  337.         // and won't work
  338.         
  339.         err = FindFolder( reply.sfFile.vRefNum, kTemporaryFolderType, kCreateFolder, &tempVRef, &tempDirID );
  340.         if ( err != noErr )
  341.             return err;  // for now, do nothing, just return        
  342.         
  343.         // make an FSSpec for the temp file
  344.         
  345.         err = FSMakeFSSpec( tempVRef, tempDirID, tempFileName, &tempFileSpec );
  346.         if ( (err != noErr) && (err != fnfErr ) )
  347.             return err;
  348.             
  349.         tempFile = new TFile(tempFileSpec);
  350.         
  351.         err = tempFile->CreateNewFile('trsh', 'trsh', smSystemScript );
  352.         
  353.         err = FSpSetFInfo( &tempFileSpec, &fileInfo );
  354.         if ( err != noErr )
  355.             {
  356.             goto cleanup;
  357.             }
  358.             
  359.         dataForkRefNum = tempFile->OpenDataFork(fsWrPerm);
  360.         }
  361.     else
  362.         {
  363.         fBackingFile->SetSpecifier(reply.sfFile);
  364.         err = fBackingFile->CreateNewFile('75SL', 'TEXT', smSystemScript );
  365.         
  366.         dataForkRefNum = fBackingFile->OpenDataFork(fsWrPerm);
  367.         }
  368.         
  369.     err = SetEOF( dataForkRefNum, 0 );
  370.     if ( err != noErr )
  371.         goto cleanup;
  372.         
  373.     // Write your file’s data into the data fork
  374.     
  375.     err = WriteDocumentDataFork();
  376.         
  377.     if (reply.sfReplacing)
  378.         err = tempFile->CloseDataFork();
  379.     else
  380.         err = fBackingFile->CloseDataFork();
  381.         
  382.     if ( err != noErr )
  383.         goto cleanup;
  384.  
  385.     if ( reply.sfReplacing )
  386.         resForkRefNum = tempFile->OpenResourceFork( fsWrPerm );
  387.     else
  388.         resForkRefNum = fBackingFile->OpenResourceFork( fsWrPerm );
  389.  
  390.     err = ResError();
  391.     if ( err != noErr )
  392.         goto cleanup;
  393.         
  394.     // Write your file’s resources into the resource fork
  395.     
  396.     err = WriteDocumentResourceFork();    
  397.  
  398.     if ( reply.sfReplacing )
  399.         err = tempFile->CloseResourceFork();
  400.     else
  401.         err = fBackingFile->CloseResourceFork();
  402.  
  403.     if ( err != noErr )
  404.         goto cleanup;
  405.     
  406.     if ( reply.sfReplacing )
  407.         {
  408.         // Everything is written, so swap the files
  409.         
  410.         err = FSpExchangeFiles( &tempFileSpec, &reply.sfFile );
  411.         if ( err != noErr )
  412.             goto cleanup;
  413.         
  414.         // can the temp file since we don't need it anymore
  415.         
  416.         tempFile->SetSpecifier(tempFileSpec);
  417.         err = tempFile->Delete();
  418.         if ( err != noErr )
  419.             goto cleanup;            
  420.         }
  421.  
  422.     // and update the disk with any unwritten data
  423.     
  424.     FlushVol( "\p", reply.sfFile.vRefNum );
  425.     
  426.     pcpy(fDocumentName, reply.sfFile.name);
  427.     SetWTitle(fWindow, fDocumentName);
  428.  
  429. cleanup:
  430.     
  431.     if (tempFile)
  432.         delete tempFile;
  433.         
  434.     return err;
  435. }
  436.  
  437.  
  438.  
  439. /**********************************************************************************/
  440.  
  441.  
  442.  
  443. OSErr TDocumentWindow::Revert()
  444. {
  445.     return -1;
  446. }
  447.  
  448.  
  449.  
  450. /**********************************************************************************/
  451.  
  452.  
  453.  
  454. #pragma mark === Printing Methods ===
  455.  
  456. void TDocumentWindow::PageSetup(Boolean useCustomPageSetup)
  457. {
  458.     Boolean                result;
  459.     gxEditMenuRecord    editMenuRec;
  460.     gxFormat            pageFormat;
  461.     Boolean                newPgFormat = false;
  462.     OSErr                err;
  463.  
  464.     if (gHasQuickDrawGX)                        // QuickDraw GX is being used.
  465.         {
  466.         //    Fill in the location of the application’s Edit menu items
  467.         //    and enable the appropriate menu items. 
  468.  
  469.         editMenuRec.editMenuID    = mEdit;
  470.         editMenuRec.cutItem        = iCut;
  471.         editMenuRec.copyItem    = iCopy;
  472.         editMenuRec.pasteItem    = iPaste;
  473.         editMenuRec.clearItem    = iClear;
  474.         editMenuRec.undoItem    = iUndo;
  475.  
  476.         gMenuBar->HiliteMenusForModalDialog(false);
  477.         
  478.         if (useCustomPageSetup)
  479.             {
  480.             //    If we have an existing page format, we'll modify that.
  481.             //    Otherwise, we'll need to create a new format and use that.
  482.  
  483.             if (fPageFormats->GetIndexedElement(fCurrentPage - 1) != nil)
  484.                 pageFormat = (gxFormat)fPageFormats->GetIndexedElement(fCurrentPage - 1);
  485.             else
  486.                 {
  487.                 pageFormat = GXNewFormat(fPrintJob);
  488.                 newPgFormat = true;
  489.                 err = GXGetJobError(fPrintJob);
  490.                 }
  491.  
  492.             // If no errors, display the format dialog.
  493.  
  494.             if (err == noErr)
  495.                 {
  496.                 result = GXFormatDialog(pageFormat, &editMenuRec, nil);
  497.  
  498.                 switch (result)
  499.                     {
  500.                     /*    If the user selected "Remove", use the default job format
  501.                         with this page.  For our application, we indicate this by
  502.                         storing nil for the format reference in our structure.
  503.                         
  504.                         If "OK" was selected, store the new format with this page.
  505.                     */
  506.                     case gxRevertSelected:
  507.                         GXDisposeFormat(pageFormat);
  508.                         pageFormat = nil;
  509.  
  510.                     case gxOKSelected:
  511.                         fPageFormats->SetIndexedElement(fCurrentPage - 1, (ArrayElementPtr)pageFormat);
  512.                         break;
  513.  
  514.                     //    If the user selected "Cancel", dispose of our cloned copy
  515.                     //    of the default job format, if we made one.
  516.  
  517.                     case gxCancelSelected:
  518.                         if (newPgFormat)
  519.                             GXDisposeFormat(pageFormat);
  520.                         break;
  521.                     }
  522.                 }
  523.             }
  524.         else
  525.             {
  526.             GXJobDefaultFormatDialog(fPrintJob, &editMenuRec);
  527.             }
  528.             
  529.         gMenuBar->HiliteMenusForModalDialog(true);
  530.         }
  531.     else                                    // QuickDraw GX is not being used.
  532.         {
  533.         PrOpen();
  534.         PrValidate(fPrintRecord);
  535.         PrStlDialog(fPrintRecord);
  536.         PrClose();
  537.         }
  538. }
  539.  
  540.  
  541.  
  542. /**********************************************************************************/
  543.  
  544.  
  545.  
  546. OSErr TDocumentWindow::Print(Boolean usePrintJobDialog)
  547. {
  548.     OSErr                err = noErr;
  549.     gxEditMenuRecord    editMenuRec;
  550.     gxDialogResult        result;
  551.     
  552.     
  553.     if (!fCanPrint)
  554.         return -1;
  555.  
  556.     if (gHasQuickDrawGX)
  557.         {
  558.         if (usePrintJobDialog)
  559.             {
  560.             /*
  561.                 If QuickDraw GX is present, fill in the location of the application's
  562.                 Edit menu items, enable/disable appropriate menu items, and display
  563.                 the Print dialog.  If the user clicks the Print button, print using
  564.                 QuickDraw GX.
  565.             */
  566.             
  567.             editMenuRec.editMenuID    = mEdit;
  568.             editMenuRec.cutItem        = iCut;
  569.             editMenuRec.copyItem    = iCopy;
  570.             editMenuRec.pasteItem    = iPaste;
  571.             editMenuRec.clearItem    = iClear;
  572.             editMenuRec.undoItem    = iUndo;
  573.         
  574.             gMenuBar->HiliteMenusForModalDialog(false);
  575.             result = GXJobPrintDialog(fPrintJob, &editMenuRec);
  576.             gMenuBar->HiliteMenusForModalDialog(true);
  577.  
  578.             if (result == gxOKSelected)
  579.                 err = GXPrintLoop();    // Print using QuickDraw GX
  580.             }
  581.         else    // Print One
  582.             {
  583.             OSErr                    err;
  584.             Collection                jobCollection;
  585.             gxCopiesInfo            copiesInfo;
  586.             gxFileDestinationInfo    destInfo;
  587.             gxPageRangeInfo            pageRangeInfo;
  588.             Ptr                        oldCopiesInfo = nil, oldPageRangeInfo = nil, oldDestInfo = nil;
  589.             long                    oldCopiesSize, oldPageRangeInfoSize, oldDestInfoSize;
  590.  
  591.             // Get the job collection and set it up to print one copy…
  592.  
  593.             jobCollection = GXGetJobCollection(fPrintJob);
  594.  
  595.             // Set number of copies to 1.
  596.             
  597.             copiesInfo.copies = 1;
  598.             err = ReplaceCollectionItem(&copiesInfo, sizeof(gxCopiesInfo),
  599.                                             gxCopiesTag, gxPrintingTagID,
  600.                                             jobCollection, &oldCopiesInfo, &oldCopiesSize);
  601.             nrequire(err, ReplaceCopies_error);
  602.  
  603.             // Set page range to "all".
  604.  
  605.             pageRangeInfo.simpleRange.optionChosen = gxDefaultPageRange;
  606.             pageRangeInfo.minFromPage = 1;
  607.             pageRangeInfo.simpleRange.fromPage = 1;
  608.             pageRangeInfo.maxToPage = fNumOfPages;
  609.             pageRangeInfo.simpleRange.toPage = fNumOfPages;
  610.             pageRangeInfo.simpleRange.printAll = true;
  611.             err = ReplaceCollectionItem(&pageRangeInfo, sizeof(gxPageRangeInfo),
  612.                                             gxPageRangeTag, gxPrintingTagID,
  613.                                             jobCollection, &oldPageRangeInfo, &oldPageRangeInfoSize);
  614.             nrequire(err, ReplacePageRange_error);
  615.  
  616.             // Set destination to "printer".
  617.  
  618.             destInfo.toFile = false;
  619.             err = ReplaceCollectionItem(&destInfo, sizeof(gxFileDestinationInfo),
  620.                                           gxFileDestinationTag, gxPrintingTagID,
  621.                                           jobCollection, &oldDestInfo, &oldDestInfoSize);
  622.             nrequire(err, ReplaceDestination_error);
  623.  
  624.             // Print one copy of our document.
  625.  
  626.             err = GXPrintLoop();
  627.  
  628.             /*
  629.                 Restore original number of copies, page range, and output
  630.                 destination in case anybody uses that info.
  631.             */
  632.  
  633. ReplaceDestination_error:
  634.             ReplaceCollectionItem(oldDestInfo, oldDestInfoSize,
  635.                                     gxFileDestinationTag, gxPrintingTagID,
  636.                                     jobCollection, nil, nil);
  637. ReplacePageRange_error:
  638.             ReplaceCollectionItem(oldPageRangeInfo, oldPageRangeInfoSize,
  639.                                     gxPageRangeTag, gxPrintingTagID,
  640.                                     jobCollection, nil, nil);
  641. ReplaceCopies_error:
  642.             ReplaceCollectionItem(oldCopiesInfo, oldCopiesSize,
  643.                                       gxCopiesTag, gxPrintingTagID,
  644.                                     jobCollection, nil, nil);
  645.  
  646.             // Dispose of the pointers that MyReplaceCollectionItem created.
  647.  
  648.             if (oldCopiesInfo)
  649.                 DisposePtr(oldCopiesInfo);
  650.  
  651.             if (oldPageRangeInfo)
  652.                 DisposePtr(oldPageRangeInfo);
  653.  
  654.             if (oldDestInfo)
  655.                 DisposePtr(oldDestInfo);
  656.             }
  657.         }
  658.     else
  659.         {
  660.         //    If QuickDraw GX is NOT present, open the printer driver, and put up the
  661.         //    old Print dialog.  If the user clicks the Print button, print using
  662.         //    the Printing Manager.
  663.         
  664.         PrOpen();
  665.         PrValidate(fPrintRecord);
  666.         if (PrJobDialog(fPrintRecord))
  667.             err = QDPrintLoop();    // Print using QuickDraw
  668.         PrClose();
  669.         }
  670.  
  671.     return err;
  672. }
  673.  
  674.  
  675.  
  676. /**********************************************************************************/
  677.  
  678.  
  679.  
  680. OSErr TDocumentWindow::QDPrintLoop(void)
  681. {
  682.     OSErr            err = noErr;
  683.     TPPrPort        printPort;
  684.     TPPrStatusRef    theStatus;
  685.     short            copy, pg, iFstPage, iLstPage, oldCurPage;
  686.  
  687.  
  688.     oldCurPage = fCurrentPage;
  689.  
  690.     iFstPage = (*fPrintRecord)->prJob.iFstPage;
  691.     if (iFstPage < 1)
  692.         iFstPage = 1;
  693.  
  694.     iLstPage = (*fPrintRecord)->prJob.iLstPage;
  695.     if (iLstPage > fNumOfPages)
  696.         iLstPage = fNumOfPages;
  697.  
  698.     (*fPrintRecord)->prJob.iFstPage = 1;
  699.     (*fPrintRecord)->prJob.iLstPage = 9999;
  700.  
  701.     //    Loop for the number of copies we need to make, opening a
  702.     //    document and a page for each.
  703.  
  704.     for (copy = 1; !err && (copy <= (*fPrintRecord)->prJob.iCopies); copy++)
  705.         {
  706.         printPort = PrOpenDoc(fPrintRecord, nil, nil);
  707.  
  708.         if (!(err = PrError()))
  709.             for (pg = iFstPage; pg <= iLstPage; pg++)
  710.                 {
  711.                 PrOpenPage(printPort, nil);
  712.  
  713.                 fCurrentPage = pg;
  714.                 
  715.                 DrawCurrentPage();
  716.         
  717.                 PrClosePage(printPort);
  718.                 }
  719.  
  720.         PrCloseDoc(printPort);
  721.         }
  722.  
  723.     // When finished printing, call PrPicFile (if necessary).
  724.  
  725.     if (!err)
  726.         err = PrError();
  727.  
  728.     if (!err && ((*fPrintRecord)->prJob.bJDocLoop == bSpoolLoop))
  729.         PrPicFile(fPrintRecord, nil, nil, nil, theStatus);
  730.     
  731.     fCurrentPage = oldCurPage;
  732.  
  733.     return err;
  734. }
  735.  
  736.  
  737.  
  738. /**********************************************************************************/
  739.  
  740.  
  741.  
  742. OSErr TDocumentWindow::GXPrintLoop(void)
  743. {
  744.     OSErr                err;
  745.     long                firstPage, lastPage, numPages, pg;
  746.     long                oldPage;
  747.     gxViewPort            printViewPort;
  748.     Point                patStretch = {1,1};
  749.     gxFormat            pageFormat;
  750.     Rect                everywhereRect;
  751.     GXPrintData            spoolData;
  752.  
  753.  
  754.     oldPage = fCurrentPage;
  755.  
  756.     //    Determine which pages the user selected to print.  If the user specifies
  757.     //    a page range that is greater than the actual number of pages in the document,
  758.     //    only print those pages that are actually in the document.
  759.  
  760.     GXGetJobPageRange(fPrintJob, &firstPage, &lastPage);
  761.     
  762.     if (lastPage > fNumOfPages)
  763.         lastPage = fNumOfPages;
  764.  
  765.     //    Calculate the total number of pages to print, and begin
  766.     //    printing if there are no errors.
  767.  
  768.     numPages = lastPage - firstPage +1;
  769.     err = GXGetJobError(fPrintJob);
  770.     nrequire(err, PageRangeError);
  771.  
  772.     GXStartJob(fPrintJob, fDocumentName, numPages);
  773.     err = GXGetJobError(fPrintJob);
  774.     nrequire(err, StartJobFailed);
  775.  
  776.     //    Create a new viewport for printing, and set our translator rects
  777.     //    to "wide open" so they include all data we're drawing.  For each
  778.     //    page we print, call GXStartPage, draw, and call GXFinishPage.
  779.  
  780.     SetRect(&everywhereRect, 0, 0, 32767, 32767);
  781.     printViewPort = GXNewViewPort(gxScreenViewDevices);
  782.  
  783.     for (pg = firstPage; (err == noErr) && (pg <= lastPage); pg++)
  784.         {
  785.         // Get the page's format and start printing the page.
  786.  
  787.         pageFormat = (gxFormat)fPageFormats->GetIndexedElement(pg - 1);
  788.  
  789.         if (pageFormat == nil)
  790.             pageFormat = GXGetJobFormat(fPrintJob, 1);
  791.  
  792.         GXStartPage(fPrintJob, pg, pageFormat, 1, &printViewPort);                                
  793.  
  794.         err = GXGetJobError(fPrintJob);
  795.  
  796.         //    If there were no errors, set up the Translator, draw the QuickDraw
  797.         //    data for the current page and remove the Translator.
  798.     
  799.         //    GXInstallQDTranslator actually requires a pointer to a CGrafPort,
  800.         //    and will crash if you pass it a true GrafPtr.
  801.  
  802.         nrequire(err, StartPageFailed);
  803.         spoolData.printViewPort = printViewPort;
  804.         GXGetFormatDimensions(pageFormat, &spoolData.pageArea, nil);
  805.  
  806.         GXInstallQDTranslator(fWindow,
  807.                               gxDefaultOptionsTranslation,
  808.                               &everywhereRect, &everywhereRect,
  809.                               patStretch, 
  810.                               NewgxShapeSpoolProc(PrintOneGXShape),
  811.                               &spoolData);
  812.  
  813.         fCurrentPage = pg;
  814.         SetPortWindowPort(fWindow);
  815.         DrawCurrentPage();    
  816.         GXRemoveQDTranslator(fWindow, nil);
  817.  
  818.         // Finish the page.
  819.  
  820.         GXFinishPage(fPrintJob);
  821.         }
  822.  
  823.     // Finish printing.
  824.  
  825. StartPageFailed:
  826.  
  827.     GXFinishJob(fPrintJob);
  828.     err = GXGetJobError(fPrintJob);
  829.         
  830.     GXDisposeViewPort(printViewPort);
  831.     fCurrentPage = oldPage;
  832.  
  833. StartJobFailed:
  834. PageRangeError:
  835.     return err;
  836. }
  837.  
  838.  
  839.  
  840. /**********************************************************************************/
  841.  
  842.  
  843.  
  844. void TDocumentWindow::DrawCurrentPage(void)
  845. {
  846.     // override me
  847. }
  848.  
  849.  
  850.  
  851. /**********************************************************************************/
  852.  
  853.  
  854.  
  855. #pragma mark === Static Functions ===
  856.  
  857.  
  858. static OSErr ReplaceCollectionItem(void *newData, long collectSize,
  859.                                    OSType collectType, long collectID,
  860.                                    Collection whichCollection,
  861.                                    Ptr *oldData, long *oldDataSize)
  862. {
  863.     OSErr    err;
  864.     long    index;
  865.  
  866.  
  867.     //    If we're supposed to return the old data, get it.
  868.     //    If there is no old data, return a copy of the new data.
  869.  
  870.     if (oldData)
  871.         {
  872.         err = GetCollectionItemInfo(whichCollection,
  873.                                     collectType,
  874.                                     collectID,
  875.                                     kCollectionDontWantIndex,
  876.                                     oldDataSize,
  877.                                     kCollectionDontWantAttributes);
  878.         if (err)
  879.             {
  880.             *oldDataSize = collectSize;
  881.             *oldData = NewPtrSys(*oldDataSize);
  882.             if (!(err = MemError()))
  883.                 BlockMoveData(newData, *oldData, collectSize);
  884.             }
  885.         else
  886.             {
  887.             *oldData = NewPtrSys(*oldDataSize);
  888.             if (!(err = MemError()))
  889.                 err = GetCollectionItem(whichCollection,
  890.                                         collectType,
  891.                                         collectID,
  892.                                         kCollectionDontWantSize,
  893.                                         *oldData);
  894.             }
  895.  
  896.         nrequire(err, CouldNotSetOldData);
  897.         }
  898.  
  899.     //    If we're adding a new collection item, do so.  Otherwise,
  900.     //    get the existing item's index and replace the old collection item.
  901.  
  902.     err = AddCollectionItem(whichCollection,
  903.                             collectType,
  904.                             collectID,
  905.                             collectSize,
  906.                             newData);
  907.  
  908.     if (err == collectionItemLockedErr)
  909.         {
  910.         err = GetCollectionItemInfo(whichCollection,
  911.                                     collectType,
  912.                                     collectID,
  913.                                     &index,
  914.                                     kCollectionDontWantSize,
  915.                                     kCollectionDontWantAttributes);
  916.         if (!err)
  917.             err = ReplaceIndexedCollectionItem(whichCollection,
  918.                                                index,
  919.                                                collectSize,
  920.                                                newData);
  921.         }
  922.  
  923. CouldNotSetOldData:
  924.     return err;
  925. }
  926.  
  927.  
  928.  
  929. /**********************************************************************************/
  930.  
  931.  
  932.  
  933. static OSErr PrintOneGXShape(gxShape currentShape, long refCon)
  934. {
  935.     GXPrintDataPtr        DataPtr;
  936.     gxShapeType            theShapeType;
  937.  
  938.  
  939.     //    Don't waste time spooling the shape if it’s being
  940.     //    drawn completely off the page.
  941.  
  942.     DataPtr = (GXPrintDataPtr) refCon;
  943.     theShapeType = GXGetShapeType(currentShape);
  944.     
  945.     if ((theShapeType == gxEmptyType)   ||  (theShapeType == gxFullType)    || 
  946.         (theShapeType == gxPictureType) ||    (GXTouchesBoundsShape(&DataPtr->pageArea, currentShape)))
  947.         {
  948.         GXSetShapeViewPorts(currentShape, 1, &DataPtr->printViewPort);
  949.         GXDrawShape(currentShape);
  950.         }
  951.     
  952.     return (OSErr) GXGetGraphicsError(nil);
  953. }
  954.  
  955.  
  956.  
  957. /**********************************************************************************/
  958.  
  959.  
  960.  
  961. static OSErr    CheckObjectLock(short vRefNum, long dirID, StringPtr name)
  962. {
  963.     CInfoPBRec pb;
  964.     OSErr error;
  965.     
  966.     pb.hFileInfo.ioNamePtr = name;
  967.     pb.hFileInfo.ioVRefNum = vRefNum;
  968.     pb.hFileInfo.ioDirID = dirID;
  969.     pb.hFileInfo.ioFDirIndex = 0;    // use ioNamePtr and ioDirID
  970.     error = PBGetCatInfoSync(&pb);
  971.     
  972.     if ( error == noErr )
  973.         {
  974.         // check locked bit
  975.         if ( (pb.hFileInfo.ioFlAttrib & 0x01) != 0 )
  976.             error = fLckdErr;
  977.         }
  978.     return ( error );
  979. }
  980.  
  981.